#pragma once

/*
实现具体的消息处理对象
*/

#include "AbstractType.hpp"
#include "common.hpp"
#include "Unit.hpp"
#include <vector>
namespace Message
{
    using Address = std::pair<std::string, uint16_t>;
    // 具体一个处理json字符串的对象基类出来继承于BaseMessage类
    //  然后给具体的回应和请求类型的json字符串对象继承--发送的消息都是基于json字符串的
    class JsonMessage : public Abstract::BaseMessage
    {
    public:
        // 消息的序列化和反序列化
        std::string Serialize() override
        {
            std::string body;
            int ret = JSON::Serialize(_root, body);
            if (ret == false)
                return "";

            return body;
        }
        bool UnSerialize(std::string &msg) override
        {
            return JSON::UnSerialize(_root, msg);
        }
        bool Check() override
        {
            if (_root[KEY_CODE].isNull() == true)
            {
                ILOG("该来自服务器端的响应无状态码!");
                return false;
            }
            if (_root[KEY_CODE].isIntegral() == false)
            {
                ILOG("该来自服务器端的响应状态码类型错误!");
                return false;
            }
            return true;
        }

    protected:
        Json::Value _root;
    };

    // 定义一个处理请求json字符串的对象继承JsonMesseage对象的方法
    // 由于JsonMesseage对象继承了纯虚方法如果JsonMesseage没有全部重写
    // 这些纯虚方法也是不能实例化对象的，已经重写的纯虚方法被RequestJson继承
    // 之后也不用重写直接使用即可，没有被重写的，RequestJson要重写才能实例化对象出来
    class RequestJson : public JsonMessage
    {
    public:
    };

    // 处理客户端json字符串消息
    class ResponceJson : public JsonMessage
    {
    public:
        // 获取回应的状态码
        ResCode Getcode()
        {
            return (ResCode)_root[KEY_CODE].asInt();
        }

        // 设置回应的状态码
        void Setcode(ResCode RC)
        {
            _root[KEY_CODE] = (int)RC;
        }
    };

    // 基于客户端的RPc请求的 Json字符串处理对象
    // 这个对象将来客户端和服务端使用
    class RpcRequest : public JsonMessage
    {
    public:
        using RpcPtr=std::shared_ptr<RpcRequest>;
        // rpc请求的中的json字符串包括请求方法名称(函数调用的名称) ，参数字段(一个Value对象存储)
        // 校验正文的各个字段
        bool Check() override
        {
            // 检查RPC请求正文字段中的method字段
            if (_root[KEY_METHOD].isNull() == true || _root[KEY_METHOD].isString() == false)
            {
                ILOG("该Rpc请求中没有请求方法字段或者方法字段类型错误");
                return false;
            }
            // 检查RPC请求正文字段中的参数字段
            if (_root[KEY_PRAMAS].isNull() == true || _root[KEY_PRAMAS].isObject() == false)
            {
                ILOG("该Rpc请求中没有参数字段或者参数字段类型错误");
                return false;
            }
            return true;
        }

        // 获取正文的method字段
        std::string GetMethod()
        {
            return _root[KEY_METHOD].asString();
        }
        // 获取正文字段中的函数参数
        Json::Value GetParams()
        {
            return _root[KEY_PRAMAS];
        }
        // 设置正文的函数调用名称
        void SetMethod(const std::string &method)
        {
            _root[KEY_METHOD] = method;
        }
        void SetParams(const Json::Value &para)
        {
            _root[KEY_PRAMAS] = para;
        }
    };

    // 主题类型的请求处理对象

    // 服务端请求处理和客户端构建请求处理
    class TopicRequest : public JsonMessage
    {
    public:
        using TopicPtr=std::shared_ptr<TopicRequest>;
        bool Check() override
        {
            // 检查主题请求正文字段中的主题字段和值存不存在
            if (_root[KEY_Topic].isNull() == true || _root[KEY_Topic].isString() == false)
            {
                ILOG("该topic请求中没有主题字段或者字段类型错误");
                return false;
            }
            // 检查主题请求正文字段中的主题操作字段和值存不存在
            if (_root[KEY_TOPTYPE].isNull() == true || _root[KEY_TOPTYPE].isIntegral() == false)
            {
                ILOG("该主题请求中没有主题操作字段或者字段类型错误");
                return false;
            }
            // 检查如果主题操作字段中的值为发布主题类型那么需要检查发布的内容是否存在
            if (_root[KEY_TOPTYPE].asInt() == (int)TopicOperType::Topic_SendMesg && (_root[KEY_Tmsg].isNull() == true || _root[KEY_Tmsg].isString() == false))
            {
                ILOG("该主题请求中没有主题内容字段或者字段类型错误");
                return false;
            }
            return true;
        }

        // 获取正文的topic字段值
        std::string GetTopic()
        {
            return _root[KEY_Topic].asString();
        }
        // 获取正文主题操作字段中的值
        TopicOperType GetTopOper()
        {
            return (TopicOperType)_root[KEY_TOPTYPE].asInt();
        }
        // 获取正文主题的内容字段中的值
        std::string GetTopicMsg()
        {
            return _root[KEY_Tmsg].asString();
        }
        // 设置正文的topic字段值
        void SetTopic(const std::string &topic)
        {
            _root[KEY_Topic] = topic;
        }
        // 正文主题操作字段中的值
        void SetTopOper(TopicOperType T)
        {
            _root[KEY_TOPTYPE] = (int)T;
        }

        // 正文主题操作字段中的值
        void SetTopicMsg(const std::string &msg)
        {
            _root[KEY_Tmsg] = msg;
        }
    };

    // 服务中心对象处理提供 服务注册/发现/上线/下线类型消息的功能
    // 一条Server请求的正文字段应该包括函数方法字段，服务操作字段，主机(ip,port)字段--哪个主机要
    // 请求什么服务是查询指定服务还是注册指定服务还是通知客户端下线服务或者上线服务
    class ServerRequest : public JsonMessage
    {
    public:
        using SerPtr=std::shared_ptr<ServerRequest>;
        bool Check() override
        {
            // 检查服务请求正文字段中的函数方法字段和值存不存在--你要注册的方法和查询
            if (_root[KEY_METHOD].isNull() == true || _root[KEY_METHOD].isString() == false)
            {
                ILOG("该server请求中没有主题字段或者字段类型错误");
                return false;
            }
            // 检查主题请求正文字段中的服务操作字段和值存不存在--注册服务还是查询服务
            if (_root[KEY_SerOper].isNull() == true || _root[KEY_SerOper].isIntegral() == false)
            {
                ILOG("该服务请求中没有服务操作字段或者字段类型错误");
                return false;
            }
            // 如果不是服务查询操作那么不用检查host字段的值
            //  检查服务请求的主机字段中的值
            if (_root[KEY_SerOper].asInt() == (int)ServiceOptype::SERVICE_Discover)
            {
                if (_root[KEY_HOST].isNull() == false)
                {
                    ILOG("该服务请求发现服务操作中有主机字段");
                    return false;
                }
            }

            if (_root[KEY_HOST].isNull() == true || _root[KEY_HOST].isObject() == false)
            {
                ILOG("该服务请求中没有主机字段或者字段类型错误");
                return false;
            }
            if (_root[KEY_HOST][KEY_IP].isNull() == true || _root[KEY_HOST][KEY_IP].isString() == false)
            {
                ILOG("该服务请求中没有ip字段或者字段类型错误");
                return false;
            }

            if (_root[KEY_HOST][KEY_PORT].isNull() == true || _root[KEY_HOST][KEY_PORT].isInt() == false)
            {
                ILOG("该服务请求中没有port字段或者字段类型错误");
                return false;
            }
            return true;
        }

        // 获取正文的method字段值
        std::string method()
        {
            return _root[KEY_METHOD].asString();
        }
        // 获取正文服务操作字段中的值
        ServiceOptype GetSerOper()
        {
            return (ServiceOptype)_root[KEY_SerOper].asInt();
        }
        // 获取正文的host字段中的值
        Address GetHost()
        {
            Address add;
            add.first = _root[KEY_HOST][KEY_IP].asString();
            add.second = _root[KEY_HOST][KEY_PORT].asInt();
            return add;
        }
        // 设置正文的method字段值
        void SetMethod(const std::string &method)
        {
            _root[KEY_METHOD] = method;
        }
        // 正文主题服务操作字段中的值
        void SetSerOper(ServiceOptype S)
        {
            _root[KEY_SerOper] = (int)S;
        }

        // 正文主题Host字段中的值
        void SetHost(const Address &add)
        {
            Json::Value Jadd;
            Jadd[KEY_IP] = add.first;
            Jadd[KEY_PORT] = add.second;
            _root[KEY_HOST] = Jadd;
        }
    };

    // 客户端Rpc回应处理和服务端构建回应处理,回应字段应该包括
    // 计算的状态字段和结果字段
    class RpcReponceJson : public ResponceJson
    {
    public:
        // 检查客户端收到的回应里面正文的状态响应码字段的值和类型

        // 获取计算结果字段的值
        Json::Value GetResult()
        {
            return _root[KEY_RESULT];
        }

        // 设置计算结果字段的值,如果计算结果出错就返回空的Value result对象
        void SetResult(const Json::Value& result)
        {
            _root[KEY_RESULT] = result;
        }
    };

    // 客户端Tpoic回应处理和服务端构建回应处理
    // 正文中的字段应该包括表示订阅主题，取消主题。删除主题，创建主题，发布主题内容等操作是否成功的
    // 字段和值
    class TopicResponceJson : public ResponceJson
    {
    public:
        using TPPtr=std::shared_ptr<TopicResponceJson>;
    };

    // 客户端Server回应处理和服务端构建回应处理
    // 正文字段应该包括表示服务操作状态的状态码字段，
    class ServerReponceJson : public ResponceJson
    {
    public:
        // 检查客户端收到的回应里面正文的状态响应码字段的值和类型
        bool Check() override
        {
            if (_root[KEY_CODE].isNull() == true || _root[KEY_CODE].isIntegral() == false)
            {
                ILOG("该来自服务器端的响应无状态码或者类型错误!");
                return false;
            }
            if (_root[KEY_SerOper].isNull() == true || _root[KEY_SerOper].isIntegral() == false)
            {
                ILOG("该来自服务器端的响应无服务操作字段或者值类型错误!");
                return false;
            }
            // 如果是查询服务的回应需要额外检查method字段和host字段
            if (_root[KEY_SerOper].asInt() == (int)ServiceOptype::SERVICE_Discover)
            {
                if (_root[KEY_METHOD].isNull() == true || _root[KEY_METHOD].isString() == false)
                {
                    ILOG("该来自服务器端的响应无服务名称字段或者字段值类型错误!");
                    return false;
                }
                if (_root[KEY_HOST].isNull() == true || _root[KEY_HOST].isArray() == false)
                {
                    ILOG("该来自服务器端的响应无主机字段或者字段值类型错误!");
                    return false;
                }
                return true;
            }

            return true;
        }

        // 获取服务名称字段的值，
        std::string GetMethod()
        {
            return _root[KEY_METHOD].asString();
        }

        // 设置服务名称字段的值，只有查询服务操作的回应需要设置
        void SetMethod(const std::string &method)
        {
            _root[KEY_METHOD] = method;
        }
        // 获取回应正文中服务操作字段中的值
        ServiceOptype GetSerOper()
        {
            return (ServiceOptype)_root[KEY_SerOper].asInt();
        }

        // 设置回应中正文的服务操作字段中的值--用来区分是那种服务操作的回应
        // 因为除了服务发现操作的回应是需要设置host字段的值，和获取host字段的值
        // 其它的服务操作的回应都不需要设置host字段的值的
        void SetSerOper(ServiceOptype S)
        {
            _root[KEY_SerOper] = (int)S;
        }

        // 设置服务操作结果字段的值
        // 如果是服务发现操作那么要返回所有支持该服务的host字段的值即服务器的端口和ip
        // 如果是服务上线和下线和注册操作，那么只需返回对应的操作状态码即可
        void SetDisResult(std::vector<Address>& Adds)
        {
            Json::Value Jadd;
            for (auto &Add : Adds)
            {
                Jadd[KEY_IP] = Add.first;
                Jadd[KEY_PORT] = Add.second;
                _root[KEY_HOST].append(Jadd); // host字段对应一个数值存储着一个个的主机
            }
        }
        // 客户端获取回应中的所有能够提供服务的主机
        void GetDisResult(std::vector<Address>& Adds)
        {
            Address add;
            //std::vector<Address> Adds;
            int size = _root[KEY_HOST].size();
            for (int i = 0; i < size; i++)
            {
                add.first = _root[KEY_HOST][i][KEY_IP].asString();
                add.second = _root[KEY_HOST][i][KEY_PORT].asInt();
                Adds.emplace_back(add.first, add.second);
            }
            //return Adds;
        }
    };

    class BuildMessage
    {

    public:
        static std::shared_ptr<Abstract::BaseMessage> create(Rs_RqType T)
        {
            switch (T)
            {
            case Rs_RqType::RPC_Req:
                return std::make_shared<Message::RpcRequest>();
            case Rs_RqType::RPC_Res:
                return std::make_shared<Message::RpcReponceJson>();
            case Rs_RqType::TOPIC_Req:
                return std::make_shared<Message::TopicRequest>();
            case Rs_RqType::TOPIC_Res:
                return std::make_shared<Message::TopicResponceJson>();
            case Rs_RqType::Service_Req:
                return std::make_shared<Message::ServerRequest>();
            case Rs_RqType::Service_Res:
                return std::make_shared<Message::ServerReponceJson>();
            }
            return std::shared_ptr<Abstract::BaseMessage>(nullptr);
        }

        // 使用模板和参数包构造不同的对象
        template <typename T, typename... Args>
        static std::shared_ptr<T> build(Args &&...args)
        {
            return std::make_shared<T>(std::forward<Args>(args)...);
        }
    };
}